#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   DownLoad.py
@Time    :   2023/10/19 09:16:36
@Author  :   Andy 
@Version :   1.0
@Contact :   SongLi@chengtech.com
@License :   (C)Copyright 2022-2025
@Desc    :   None
'''

import os
import sys
import logging
logging.getLogger().setLevel(logging.INFO)
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.abspath(os.path.dirname(curPath) + os.path.sep + ".")
sys.path.append(rootPath)
sys.path.append(os.path.join(rootPath,'commDriver'))
sys.path.append(os.path.join(rootPath,'data'))
# logging.info(sys.path)

from commDriver.TS_CAN import *
from commDriver.power import *
from commDriver.modbus_plc import *
from project_CAN import *
import crcmod
import math
from eprogress import LineProgress
from threading import Thread
import hashlib
import zipfile
import shutil
import Set_up
import datetime
import xml.etree.ElementTree as ET
import fnmatch

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
import hashlib




class Download(Thread):
    def __init__(self,candriver,diag,flashdrive_path,boot_path,app_path,exit=False,crcerror=False,erraddr = False, skip37 = False,
                    skipprecheck=False,skipflash=False,skipapp=False,skipboot=False,errorApp=False,errorBoot=False,skiprestart = False,isContinue = True):
        Thread.__init__(self)
        self.candriver = candriver          #can引用
        self.diag = diag                    #诊断引用--物理寻址
        # self.diagfun = diagfun              #诊断引用--功能寻址(20240517开始，新版功能寻址在dll函数已兼容)
        self.flashdrive_path = flashdrive_path            #flashdrive_path文件路径
        self.boot_path=boot_path            #boot_path文件路径
        self.app_path=app_path              #app_path文件路径
        self.stop_flag=False                #外部条件停止刷写过程
        self.progress_number = 0            #刷写进度条
        self.success_flag = False           #刷写成功标志
        self.app_success_flag = False       #app成功标志
        self.boot_success_flag = False      #boot成功标志
        self.finish = False                 #刷写完成标志位
        self.normal = False                 #刷写异常后能否正常开机
        self.app = False                    #是否为APP文件刷写
        self.boot = False                   #是否为boot文件刷写        
        self.erashing = False               #正在擦除内存标志
        self.erash = False                  #是否擦除内存标志
        self.exit = exit                    #是否退出当前boot模式 -- 刷写前
        self.crcerror=crcerror              #是否用错误的crc数据
        self.skipprecheck=skipprecheck      #是否跳过预刷写条件检测
        self.skipflash=skipflash            #是否跳过Flash Driver下载
        self.skipboot=skipboot              #是否跳过boot下载  
        self.skipapp=skipapp                #是否跳过app下载     
        self.startaddress = 0               #BIN文件起始地址
        self.size = 0                       #BIN文件大小
        self.num1 = 0                       #应用软件编程尝试次数
        self.num2 = 0                       #应用软件编程成功次数
        self.exitflash = False              #预编程不通过，直接退出刷写
        self.erraddr = erraddr              #是否擦除时用错误地址
        self.skip37 = skip37                #是否跳过37服务
        self.skiprestart = skiprestart      #是否跳过重启
        self.isContinue = isContinue        #是否继续刷写
        self.file_type = ''                 #文件类型
        self.appSIG = ''
        self.bootSIG = ''
        self.flashdriverSIG = ''
        self.start()
        self.key = 'BE12'

    # 从文件读取私钥
    def load_private_key(self,file_path):
        with open(file_path, "rb") as key_file:
            private_key = serialization.load_pem_private_key(
                key_file.read(),
                password=None,  # 如果私钥是加密的，需要提供密码
                backend=default_backend()
            )
        return private_key

    # 获取公钥
    def get_public_key(self,private_key):
        public_key = private_key.public_key()
        return public_key

    def calculate_sha256(self,data):
        sha256 = hashlib.sha256()
        sha256.update(data.encode('utf-8'))
        return sha256.hexdigest()
    

    # 签名函数
    def sign_message(self, message):

        private_key = self.load_private_key(Set_up.pubpem)
        # 使用PSS填充进行签名
        signature = private_key.sign(
            message,
            padding.PSS(
                mgf=padding.MGF1(hashes.SHA256()),
                salt_length=padding.PSS.MAX_LENGTH
            ),
            hashes.SHA256()
        )
        signature = signature.hex()
        return signature

    def parse_file(self,file_path):
        self.file_type = os.path.splitext(file_path)[-1]
        self.binfile_handle = bincopy.BinFile()
        if(self.file_type == '.hex' or self.file_type == '.HEX'):
            with open(file_path, 'r') as fin:
                self.binfile_handle.add_ihex(fin.read())
            self.size = self.binfile_handle.segments.maximum_address - self.binfile_handle.segments.minimum_address
        if(self.file_type == '.bin' or self.file_type == '.BIN'):
            with open(file_path, 'rb') as fin:
                self.binfile_handle.add_binary(fin.read())
        if(self.file_type == '.s19'):
            with open(file_path, 'r') as fin:
                self.binfile_handle.add_srec(fin.read())
        if(self.file_type == '.xml' or self.file_type == '.XML'):
            # 读取 XML 文件
            tree = ET.parse(file_path)
            root = tree.getroot()

            # 遍历每个 <list> 元素
            for list in root.findall('list'):
                fileName = list.find('fileName').text  # 找到 <name> 元素并获取文本
                fileSIG = list.find('fileSIG').text      # 找到 <age> 元素并获取文本
                if fileName.find('1-') >= 0:
                    self.flashdriverSIG = fileSIG
                if fileName.find('2-') >= 0:
                    self.appSIG = fileSIG
                if fileName.find('BOOT') >= 0:
                    self.bootSIG = fileSIG
            logging.info(f'fileName: {fileName}, fileSIG: {fileSIG}')

        return self.binfile_handle.segments

    def download(self,path):
        download_data=[]
        self.parse_file(path)
        for index,segment in enumerate(self.binfile_handle.segments):
            if(self.file_type == '.bin' or self.file_type == '.BIN'):
                start_address = self.startaddress
                segment_size = self.size
            else:
                start_address = segment.minimum_address
                segment_size = segment.maximum_address - segment.minimum_address

            # 31 Server,Erash Memory               
            if self.erash == True:                
                self.erashing = True
                time.sleep(1)
                if self.erraddr == True:
                    Request_download_CMD = [0x31,0x01,0xFF,0x00,0x44] + \
                                        [0x00,0x78,0x00,0x00] +\
                                        [(segment_size>>24) & 0xFF] + [(segment_size>>16) & 0xFF] + [(segment_size>>8) & 0xFF] + [(segment_size) & 0xFF]
                else:
                    Request_download_CMD = [0x31,0x01,0xFF,0x00,0x44] + \
                                        [(start_address>>24) & 0xFF] + [(start_address>>16) & 0xFF] + [(start_address>>8) & 0xFF] + [(start_address) & 0xFF] +\
                                        [(segment_size>>24) & 0xFF] + [(segment_size>>16) & 0xFF] + [(segment_size>>8) & 0xFF] + [(segment_size) & 0xFF]
                rep_value,rep_size = self.diag.req_and_res_can(Request_download_CMD,len(Request_download_CMD),enable_print= True)
                if rep_value[0:5] == [0x71,0x01,0xFF,0x00,0x00]:
                    logging.info('Erash Memory ok')
                else:
                    logging.info('Erash Memory fail')
                time.sleep(1)
                self.erashing = False
                
            # 34 Server,Request download
            Request_download_CMD = [0x34,0x00,0x44] + \
                                    [(start_address>>24) & 0xFF] + [(start_address>>16) & 0xFF] + [(start_address>>8) & 0xFF] + [(start_address) & 0xFF] +\
                                    [(segment_size>>24) & 0xFF] + [(segment_size>>16) & 0xFF] + [(segment_size>>8) & 0xFF] + [(segment_size) & 0xFF]
            rep_value,rep_size = self.diag.req_and_res_can(Request_download_CMD,len(Request_download_CMD),enable_print= True)           

            if rep_value[0:1] == [0x74]:
                lengthformatidentifier = rep_value[1]
                logging.info('34 server data format is: {}'.format(lengthformatidentifier))
                if lengthformatidentifier == 0x20:
                    maxnumberofblocklength = rep_value[2]*256+rep_value[3]
                else:
                    maxnumberofblocklength = rep_value[4]*256+rep_value[5]
                logging.info(f'maxnumberofblocklength is: {maxnumberofblocklength}')
                # 36 Server, start download
                if maxnumberofblocklength < 0x1FFF:
                    numberofblockdatalength = maxnumberofblocklength-2
                    data_36_send_times = math.ceil(segment_size/numberofblockdatalength)
                    logging.info(f'data_36 send times is: {data_36_send_times}') 
                    # line progress
                    line_progress = LineProgress(title='upgrade progress')
                    try:
                        for i in range(data_36_send_times):
                            self.progress_number = ((i+1)/data_36_send_times)*100
                            line_progress.update(self.progress_number)
                            blocksequencecounter = (i+1)%256
                            if i == (data_36_send_times - 1):
                                download_data_CMD = [0x36,blocksequencecounter]+list(segment.data)[i*numberofblockdatalength:]
                            else:
                                download_data_CMD = [0x36,blocksequencecounter]+list(segment.data)[i*numberofblockdatalength:((i+1)*numberofblockdatalength)]
                            rep_value,rep_size = self.diag.req_and_res_can(download_data_CMD,len(download_data_CMD),enable_print= False)
                            # logging.info('i is: {}, rep_value is: {}, stop_flag is: {}'.format(i,rep_value,self.stop_flag))
                            logging.info('i is: {},stop_flag is: {}'.format(i,self.stop_flag))
                            if rep_value[0:1] != [0x76] or self.stop_flag == True :
                                self.success_flag = False
                                raise Exception('36 Server negative response,download termination!')
                    except Exception as e:
                        logging.info(e)
            else:
                self.success_flag = False
                logging.info('34 Server negative response!')

            # 37 Server, stop download
            download_data = download_data + list(segment.data)
            if self.app and self.skip37:
                logging.info('Skip 37 Server!')
            else:
                checksum = sum(list(segment.data)) & 0xFF
                logging.info('checksum is: '+hex(checksum))
                try:
                    rep_value,rep_size = self.diag.req_and_res_can([0x37],1,enable_print= True)
                    if rep_value[0:1] == [0x77]: # and rep_value[1] == checksum:
                        logging.info('Block {} upgrade success!'.format(index))
                    else:
                        logging.info('37 server negative response!')
                except:
                    logging.info('37 server no response!')

        #Caculation CRC value       
        crc16_citt=crcmod.predefined.mkCrcFun('crc-ccitt-false')
        download_data_byte = bytearray(download_data)
        crc_value = self.sign_message(download_data_byte)

        return crc_value,start_address,segment_size
    
    def calcKey(self,seed,level=1):
        #BE12
        seed = bytearray(seed)
        if level == 1:
            mainkey = unhexlify('824235594325e23a1f2eb53049aa5d66')#('01020304050607080102030405060708')
        else:
            mainkey = unhexlify('65B7EAB1B35C3244E9C05C0262E73C6B')
        df = unhexlify('1F9D6DC2CCF3E59C160081E64E2D8899')
        sk = CMAC.new(mainkey,df,ciphermod=AES)
        sk = unhexlify(sk.hexdigest())
        
        token = CMAC.new(sk,seed,ciphermod=AES)
        # logging.info('1F9D6DC2CCF3E59C160081E64E2D8899'+token.hexdigest())
        token = list(unhexlify(token.hexdigest()))       
        key = list(df)+token
        return key
    
    def pad(self, text):
        """
        #填充函数,使被加密数据的字节码长度是block_size的整数倍
        #AES128-ECB PKCS5填充,补齐到16字节,补充12个0xc
        """
        s = ''
        for i in text:
            if i > 15:
                s += hex(i)[2:] # 将seed转化成'6816b4d5'
            else:
                s += '0'
                s += hex(i)[2:]
        logging.info(f's:{s}')
        count = len(text) # seed 固定为4
        add = self.length - (count % self.length) # 所以需要填充12个
        # entext = s + (chr(add) * add)
        entext = s + ('0c'*add)
        return entext
    
    def seedkey_410(self,seed,level=1):
        # factor_level1 = '01020304050607080102030405060708'   # '01020304050607080102030405060708''CCBA77AAE66C46DCF61FDA35A5BB7049' 
        factor_level1 = '62A984BD7D49AB1A7DB243FCBD3EB384'

        factor = factor_level1
        sha256 = hashlib.sha256(bytes(seed))
        seed1 = sha256.hexdigest().upper() + factor  # '1D26...0708'
        # logging.info(seed1)
        sha256_1 = hashlib.sha256(bytes.fromhex(seed1)) # 通过SHA256对拼接数据运算生成32字节Hash值
        seed2 = sha256_1.hexdigest().upper()[:32]  # 截取32字节Hash值的前16字节作为密钥：9A536A10AF8593BD7967D419C42B5EE0
        # logging.info(seed2)

        self.length = AES.block_size  # 初始化数据块大小
        aes_mode = AES.new(bytes.fromhex(seed2), AES.MODE_ECB)  # 初始化AES,ECB模式的实例

        # logging.info(self.pad(seed),type(self.pad(seed)))
        res = aes_mode.encrypt(bytes.fromhex(self.pad(seed)))  # self.pad(seed)= '6816B4D50C0C0C0C0C0C0C0C0C0C0C0C'  type:str,转换成字节
        key = [int(hex(x), 16) for x in res]
        logging.info(key)
        # logging.info(binascii.b2a_hex(bytearray(key)))
        return key

    def pre_update(self):
        try:
            logging.info('--------pre_update----------')
            '''预刷新步骤'''
            #step1: 读取DID+扩展会话模式  
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x10,0x01],2)
            if uds_rep[0:2] == [0x50,0x01]:
                logging.info('Enter default session ok')
            else:
                logging.info('Enter default session fail') 
            time.sleep(1) 
            uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x90],3)
            try:
                readstr = bytearray(uds_rep[3:]).decode()  #F190
                logging.info('VIN is:'+readstr)
            except:
                logging.info('Read VIN error!')
            uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x91],3)
            try:
                readstr = bytearray(uds_rep[3:]).decode()  #F191
                logging.info('HW Version is:'+readstr)
            except:
                logging.info('Read HW Version error!')
            uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x99],3) #F199
            try:
                data = uds_rep[3:]
                data_str = ''
                for i in range(len(data)):
                    data_str = data_str + '%02x' % data[i]
                logging.info('Last update time is:'+data_str)
            except:
                logging.info('Read Last update time error!')

            uds_rep,datasize = self.diag.req_and_res_can([0x10,0x03],2)
            if uds_rep[0:2] == [0x50,0x03]:
                logging.info('Enter extended session ok')
            else:
                logging.info('Enter extended session fail')
            time.sleep(1)
            # uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0xA5],3)
            # self.num1 = uds_rep[3] << 24|uds_rep[4] << 16|uds_rep[5] << 8 |uds_rep[6]
            # logging.info(f'刷写前应用软件编程尝试次数:{self.num1}')
            # uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0xAA],3)
            # self.num2 = uds_rep[3] << 24|uds_rep[4] << 16|uds_rep[5] << 8 |uds_rep[6]
            # logging.info(f'刷写前应用软件编程成功次数:{self.num2}')

            # step3: 预刷新条件检查 
            if self.skipprecheck == False:  
                uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0x02,0x03],4)
                if uds_rep[0:4] == [0x71,0x01,0x02,0x03]:
                    logging.info('Check programming precondition ok')
                else:
                    logging.info('Check programming precondition fail')
                    self.exitflash = True  

            #step2: APP下安全算法          
            # uds_rep,datasize = self.diag.req_and_res_can([0x27,0x01],2)
            # if uds_rep[0:2] == [0x67,0x01]:
            #     logging.info('Request seed Level1 ok')
            #     seed = uds_rep[2:]
            #     key = self.calcKey(seed,3)
            #     uds_rep,datasize = self.diag.req_and_res_can([0x27,0x02]+key,34)
            #     if uds_rep[0:2] == [0x67,0x02]:
            #         logging.info('Send key level1 ok')
            #     else:
            #         logging.info('Send key level1 fail')
            # else:
            #     logging.info('Request seed Level1 fail')
            # uds_rep,datasize = self.diag.req_and_res_can([0x2E,0xF1,0x90,0x4C,0x56,0x56,0x44,0x43,0x31,0x31,0x42,0x36,0x41,0x44,0x35,0x32,0x34,0x32,0x38,0x36],20)
            # if uds_rep[0:3] == [0x6E,0xF1,0x90]:
            #     logging.info('Write VIN ok')
            # else:
            #     logging.info('Write VIN fail')


            #step4: 停止诊断服务
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x85,0x82],2)
            #step5: 通信停止收发
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x28,0x83,0x01],3)
            #step6: 编程会话模式
            uds_rep,datasize = self.diag.req_and_res_can([0x10,0x02],2)
            if uds_rep[0:2] == [0x50,0x02]:
                logging.info('Enter programming session ok')
            else:
                logging.info('Enter programming session fail')
            time.sleep(1)
            # uds_resp, size = self.diag.req_and_res_can([0x22, 0xF1, 0x86], 3)
            # if uds_rep[0:4] == [0x62,0xF1,0x86, 0x02]:
            #     logging.info('Check programming session ok')
            # else:
            #     logging.info('Check programming session fail') 
            #step7: BOOT下安全算法
            time.sleep(1)
            uds_rep,datasize = self.diag.req_and_res_can([0x27,0x11],2)
            if uds_rep[0:2] == [0x67,0x11]:
                logging.info('Request seed Level2 ok')
                seed = uds_rep[2:]
                if self.key == '410':
                    key = self.seedkey_410(seed)
                    uds_rep,datasize = self.diag.req_and_res_can([0x27,0x12]+key,18)
                else:
                    key = self.calcKey(seed,3)
                    uds_rep,datasize = self.diag.req_and_res_can([0x27,0x12]+key,34)
                if uds_rep[0:2] == [0x67,0x12]:
                    logging.info('Send key level2 ok')
                else:
                    logging.info('Send key level2 fail')
            else:
                logging.info('Request seed Level2 fail') 
            #step8: 写入诊断设备序列号(0xF198)+刷新日期 (0xF199)
            # uds_rep,datasize = self.diag.req_and_res_can([0x2E,0xF1,0x99]+Set_up.boottime,7)
            # if uds_rep[0:3] == [0x6E,0xF1,0x99]:
            #     logging.info('Write update time ok')
            # else:
            #     logging.info('Write update time fail')          
            #step9: 判断是否退出boot模式
            if self.exit:
                uds_rep,datasize = self.diag.req_and_res_can([0x10,0x01],2)
                if uds_rep[0:2] == [0x50,0x01]:
                    logging.info('Exit boot mode ok')
                else:
                    logging.info('Exit boot mode fail')
        except:
            logging.info('Pre_update fail, CAN Communication Error!')

    def flash_driver_update(self):
        #step:1/2/3: 34、36、37服务更新flash Drive 
        self.erash = False           #flash Drive No need erash
        logging.info('--------Start Flash Driver Download----------')
        check_crc_flash,start_address,segment_size = self.download(self.flashdrive_path)   
        # #step4: 检查传输行列  NATA flash Drive No need crc check
        # crc_flash=[0,0,0,0]
        # crc_flash[0] = ((check_crc_flash)>>24) & 0xFF
        # crc_flash[1] = ((check_crc_flash)>>16) & 0xFF
        # crc_flash[2] = ((check_crc_flash)>>8) & 0xFF
        # crc_flash[3] = (check_crc_flash) & 0xFF
        # try:
        #     uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0xFD,0x03]+crc_flash,8)
        #     if uds_rep[0:5] == [0x71,0x01,0xFD,0x03,0x00]:
        #         logging.info('Check FlashDrive transmit sequence ok')
        #     else:
        #         logging.info('Check FlashDrive transmit sequence fail')
        # except:
        #     logging.info('31 server check FlashDrive transmit sequence no response!')

    def boot_update(self):
        self.progress_number = 0
        self.boot = True            #boot file
        self.erash = True           #boot need erash
        time.sleep(0.1)
        #step1/2/3/4: 31、34、36、37服务下载程序
        logging.info('--------Start BootLoad Download----------')
        check_crc_boot,start_address,segment_size = self.download(self.boot_path) 
        self.erash = False  
        self.boot = False            #boot file flash done  
        #step5: 获取擦除地址和长度
        eras_startaddress = [0x00,0x00,0x00,0x00]
        eras_startaddress[0] = ((start_address)>>24) & 0xFF
        eras_startaddress[1] = ((start_address)>>16) & 0xFF
        eras_startaddress[2] = ((start_address)>>8) & 0xFF
        eras_startaddress[3] = (start_address) & 0xFF
        erash_size = [0x00,0x00,0x00,0x00]
        erash_size[0] = ((segment_size)>>24) & 0xFF
        erash_size[1] = ((segment_size)>>16) & 0xFF
        erash_size[2] = ((segment_size)>>8) & 0xFF
        erash_size[3] = (segment_size) & 0xFF
        time.sleep(1)
        #step6: 检查传输行列
        if self.crcerror:
            crc_boot=[0,0]
        else:
            sr = self.strToToHexBytes(self.bootSIG)
            # crc_boot=[0,0]
            # crc_boot[0] = ((check_crc_boot)>>8) & 0xFF
            # crc_boot[1] = (check_crc_boot) & 0xFF
        
            # sr = self.strToToHexBytes(self.bootSIG)
            # logging.info(f'bootSIG: {self.bootSIG}')
            # sr = check_crc_boot
            # logging.info(f'sr: {sr}')
            # logging.info('len: %s', len(sr))
        try:
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x3E,0x80],2)
            uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0x02,0x02] + sr,260)
            if uds_rep[0:5] == [0x71,0x01,0x02,0x02,0x00]:
                logging.info('Check boot transmit sequence ok')
                self.boot_success_flag = True               
            else:
                logging.info('Check boot transmit sequence fail')                 
        except:
            logging.info('Check boot transmit and programming Dependencies no response!')
            self.finish = False                
   
    def app_update(self):
        self.progress_number = 0 
        self.app = True             #app
        self.erash = True           #app need erash
        time.sleep(0.1)
        #step1/2/3/4: 31、34、36、37服务下载程序
        logging.info('--------Start App Download----------')
        check_crc_app,start_address,segment_size = self.download(self.app_path)
        self.erash = False
        self.app = False            #APP DONE 
        #step5: 获取擦除地址和长度
        eras_startaddress = [0x00,0x00,0x00,0x00]
        eras_startaddress[0] = ((start_address)>>24) & 0xFF
        eras_startaddress[1] = ((start_address)>>16) & 0xFF
        eras_startaddress[2] = ((start_address)>>8) & 0xFF
        eras_startaddress[3] = (start_address) & 0xFF
        erash_size = [0x00,0x00,0x00,0x00]
        erash_size[0] = ((segment_size)>>24) & 0xFF
        erash_size[1] = ((segment_size)>>16) & 0xFF
        erash_size[2] = ((segment_size)>>8) & 0xFF
        erash_size[3] = (segment_size) & 0xFF   
        #step6: 检查传输行列
        if self.crcerror:
            crc_app=[0,0]
        else:
            sr = self.strToToHexBytes(self.appSIG)
            # crc_app=[0,0]
            # crc_app[0] = ((check_crc_app)>>8) & 0xFF
            # crc_app[1] = (check_crc_app) & 0xFF
        try:
            # sr = self.strToToHexBytes(self.appSIG)
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x3E,0x80],2)
            uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0x02,0x02] + sr,260)
            if uds_rep[0:5] == [0x71,0x01,0x02,0x02,0x00]:
                logging.info('Check APP transmit sequence ok')
                self.app_success_flag = True               
            else:
                self.app_success_flag = False
                logging.info('Check APP transmit sequence fail')               
        except:
            logging.info('Check APP transmit and programming Dependencies no response!')
            self.finish = False  

    def hexArrayToStr(self,hexArray):
        # 将十六进制数组转换为十六进制字符串
        hex_string = ''.join(format(byte, '02x') for byte in hexArray)
        return hex_string



    '''转换字符串为数组'''
    def strToToHexBytes(self,hexString):
        hexString = str(hexString).replace(' ', '').replace('[', '').replace(']', '').replace('\'', '')

        if ((len(hexString) % 2) != 0):
            hexString += " "

        c_16_temp = list(hexString)
        ilen = int(len(c_16_temp) / 2)
        hexBytes = [0] * ilen
        for index in range(ilen):
            hexBytes[index] = int((c_16_temp[2 * index] + c_16_temp[2 * index + 1]),16)
        return hexBytes             
        
    def post_update(self):
        try:
            logging.info('--------post_update----------')
            # #step20: 检查软硬件一致性 NATA no need
            # uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0xFD,0x01],4)
            # if uds_rep[0:5] == [0x71,0x01,0xFD,0x01,0x00]:
            #     self.success_flag = True
            #     logging.info('Check programming Dependencies ok')
            # else:
            #     self.success_flag = False
            #     logging.info('Check programming Dependencies fail')
            #step0: 硬件重启
            self.success_flag = self.app_success_flag and self.boot_success_flag
            uds_rep,datasize = self.diag.req_and_res_can([0x11,0x01],2)
            if uds_rep[0:2] == [0x51,0x01]:
                logging.info('Hardware reset ok') 
            else:
                self.success_flag = False
                logging.info('Hardware reset fail')

            time.sleep(5)
            '''后刷新步骤'''
            #step1: 进入扩展会话
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x10,0x83],2)
            time.sleep(1)
            #step2: 恢复通信
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x28,0x80,0x01],3)
            #step3: 开启DTC 
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x85,0x81],2)
            #step4: clearDTC 
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x14,0xFF,0xFF,0xFF],4)
            if uds_rep[0:1] == [0x54]:
                logging.info('Clear DTC success')
            else:
                self.success_flag = False
                logging.info('Clear DTC fail')
            #step5: 进入默认会话
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x10,0x81],2)
            time.sleep(1)
            self.finish = True 
        except:
            self.success_flag = False
            self.finish = False 
            logging.info('Post_update, CAN Communication Error!')

    def post_abnormal_termination_startup(self):            #刷写失败后验证能否正常启动
        logging.info('--------刷写后验证能否正常启动----------')
        time.sleep(2)
        for i in range(6):
            uds_rep,datasize = self.diag.req_and_res_can([0x10,0x01],2)
            if len(uds_rep) < 2:
                self.normal =False
            if uds_rep[0:2] == [0x50,0x01]:
                self.normal = True
                break
            else:
                self.normal = False
            time.sleep(1)

        uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x83],3)
        if uds_rep[0:] == [0x62,0xF1,0x83] + Set_up.BootloaderSoftwareVersionNumber:
            self.normal = True
            pass
        else:
            logging.info('boot version failed')
            self.normal = False
        
        uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x95],3)
        if uds_rep[0:] == [0x62,0xF1,0x95] + Set_up.ECUSoftwareversionnumber:
            self.normal = True
            pass
        else:
            logging.info('app version failed')
            self.normal = False

    def run(self):
        # 解析XML
        # 指定要搜索的目录
        directory = os.path.abspath(os.path.join(os.getcwd(),"./sw")) + '\\'

        # 遍历目录，查找所有的 XML 文件
        xml_files = ''
        for root, dirs, files in os.walk(directory):
            for filename in fnmatch.filter(files, '*.xml'):
                xml_files = (os.path.join(root, filename))
                if xml_files != '':
                    break
        self.parse_file(xml_files)

        self.pre_update()
        if self.exitflash == False:
            if self.skipflash == False:
                self.flash_driver_update()
            if self.skipboot == False:
                self.boot_update()
            else:
                self.boot_success_flag = True
            if self.skipapp == False:
                self.app_update()
            else:
                self.app_success_flag = True
            if self.skiprestart == False:
                self.post_update()
            else:
                self.success_flag = True

    def set_stop(self,flag):
        if self.app or self.boot:
            self.stop_flag = flag

class DownloadCAL(Thread):
    def __init__(self,candriver,diag,flashdrive_path,cal_path,exit=False,crcerror=False, erraddr=False,skipprecheck=False,skipflash=False, skip37=False):
        Thread.__init__(self)
        self.candriver = candriver          #can引用
        self.diag = diag                    #诊断引用--物理寻址
        self.flashdrive_path = flashdrive_path            #flashdrive_path文件路径
        self.cal_path=cal_path              #cal_path文件路径
        self.stop_flag=False                #外部条件停止刷写过程
        self.progress_number = 0            #刷写进度条
        self.success_flag = False           #刷写成功标志
        self.normal = False                 #刷写异常后能否正常开机
        self.erashing = False               #正在擦除内存标志
        self.erash = False                  #是否擦除内存标志
        self.exit = exit                    #退出当前boot模式
        self.crcerror=crcerror              #crc校验错误
        self.erraddr = erraddr              #擦除错误地址
        self.skipprecheck=skipprecheck      #跳过预刷写条件检测
        self.skipflash=skipflash            #跳过Flash Driver下载
        self.skip37 = skip37               #跳过37服务
        self.finish = False                 #刷写完成
        self.startaddress = 0               #BIN文件起始地址
        self.size = 0                       #BIN文件大小
        self.exitflash = False              #预编程不通过，退出刷写                
        self.cal = False                    #cal文件选择
        self.start()

    def parse_file(self,file_path):
        self.file_type = os.path.splitext(file_path)[-1]
        self.binfile_handle = bincopy.BinFile()
        if(self.file_type == '.hex' or self.file_type == '.HEX'):
            with open(file_path, 'r') as fin:
                self.binfile_handle.add_ihex(fin.read())
            self.size = self.binfile_handle.segments.maximum_address - self.binfile_handle.segments.minimum_address
        if(self.file_type == '.bin' or self.file_type == '.BIN'):
            with open(file_path, 'rb') as fin:
                self.binfile_handle.add_binary(fin.read())
        if(self.file_type == '.s19'):
            with open(file_path, 'r') as fin:
                self.binfile_handle.add_srec(fin.read())
        # logging.info(binfile_handle.segments())  #All data       
        return self.binfile_handle.segments

    def download(self,path):
        download_data=[]
        self.parse_file(path)
        for index,segment in enumerate(self.binfile_handle.segments):
            if(self.file_type == '.bin' or self.file_type == '.BIN'):
                start_address = self.startaddress
                segment_size = self.size
            else:
                start_address = segment.minimum_address
                segment_size = segment.maximum_address - segment.minimum_address
                
            if self.erash == True:
                # 31 Server,Erash Memory
                self.erashing = True
                time.sleep(1)
                if self.erraddr == True:
                    Request_download_CMD = [0x31,0x01,0xFD,0x00,0x44] + \
                                        [0x00,0x78,0x00,0x00] +\
                                        [(segment_size>>24) & 0xFF] + [(segment_size>>16) & 0xFF] + [(segment_size>>8) & 0xFF] + [(segment_size) & 0xFF]
                else:
                    Request_download_CMD = [0x31,0x01,0xFD,0x00,0x44] + \
                                        [(start_address>>24) & 0xFF] + [(start_address>>16) & 0xFF] + [(start_address>>8) & 0xFF] + [(start_address) & 0xFF] +\
                                        [(segment_size>>24) & 0xFF] + [(segment_size>>16) & 0xFF] + [(segment_size>>8) & 0xFF] + [(segment_size) & 0xFF]
                rep_value,rep_size = self.diag.req_and_res_can(Request_download_CMD,len(Request_download_CMD),enable_print= True)
                time.sleep(1)
                self.erashing = False
                
            # 34 Server,Request download
            Request_download_CMD = [0x34,0x00,0x44] + \
                                    [(start_address>>24) & 0xFF] + [(start_address>>16) & 0xFF] + [(start_address>>8) & 0xFF] + [(start_address) & 0xFF] +\
                                    [(segment_size>>24) & 0xFF] + [(segment_size>>16) & 0xFF] + [(segment_size>>8) & 0xFF] + [(segment_size) & 0xFF]
            rep_value,rep_size = self.diag.req_and_res_can(Request_download_CMD,len(Request_download_CMD),enable_print= True)           

            if rep_value[0:1] == [0x74]:
                lengthformatidentifier = rep_value[1]
                maxnumberofblocklength = rep_value[4]*256+rep_value[5]
                # 36 Server, start download
                if maxnumberofblocklength<0x1fff:
                    numberofblockdatalength = maxnumberofblocklength-2
                    data_36_send_times = math.ceil(segment_size/numberofblockdatalength)
                    logging.info(f'data_36 send times is: {data_36_send_times}') 
                    # line progress
                    line_progress = LineProgress(title='upgrade progress')
                    try:
                        for i in range(data_36_send_times):
                            self.progress_number = ((i+1)/data_36_send_times)*100
                            line_progress.update(self.progress_number)
                            blocksequencecounter = (i+1)%256
                            if i == (data_36_send_times - 1):
                                download_data_CMD = [0x36,blocksequencecounter]+list(segment.data)[i*numberofblockdatalength:]
                            else:
                                download_data_CMD = [0x36,blocksequencecounter]+list(segment.data)[i*numberofblockdatalength:((i+1)*numberofblockdatalength)]
                            rep_value,rep_size = self.diag.req_and_res_can(download_data_CMD,len(download_data_CMD),enable_print= False)
                            # logging.info('i is: {}, rep_value is: {}, flag is: {}'.format(i,rep_value,self.stop_flag))
                            logging.info('i is: {},flag is: {}'.format(i,self.stop_flag))
                            if rep_value[0:1] != [0x76] or self.stop_flag == True :
                                self.success_flag = False
                                raise Exception('download termination')
                            else:
                                if self.cal:
                                    self.success_flag = True            #标定文件只能看36是否正响应。
                    except Exception as e:
                        logging.info(e)
            else:
                self.success_flag = False
                logging.info('34 Server negative response!')
            # 37 Server, stop download
            download_data = download_data + list(segment.data)
            if self.cal and self.skip37:
                logging.info('Skip 37 Server!')
            else:
                try:
                    rep_value,rep_size = self.diag.req_and_res_can([0x37],1,enable_print= False)
                    if rep_value[0:1] == [0x77]:
                        logging.info('Block {} upgrade success!'.format(index))
                    else:
                        logging.info('37 server negative response!')
                except:
                    logging.info('37 server no response!')
        crc_32=crcmod.predefined.mkCrcFun('crc-32')
        download_data_byte = bytearray(download_data)
        crc_value = binascii.crc32(download_data_byte)
        return crc_value, start_address, segment_size
    
    def calcKey(self,seed,session = 3):
        key = [0,0,0,0]
        seed = seed[0]<<24 | seed[1]<<16 | seed[2]<< 8 | seed[3]
        if session == 3:
            APP_MASK = 0x51D96F09
            tmpseed = seed
            key_1 = tmpseed ^ APP_MASK
            seed_2 = tmpseed
            seed_2 = (seed_2 & 0x55555555) << 1 ^ (seed_2 & 0xAAAAAAAA) >> 1
            seed_2 = (seed_2 ^ 0x33333333) << 2 ^ (seed_2 ^ 0xCCCCCCCC) >> 2
            seed_2 = (seed_2 & 0x0F0F0F0F) << 4 ^ (seed_2 & 0xF0F0F0F0) >> 4
            seed_2 = (seed_2 ^ 0x00FF00FF) << 8 ^ (seed_2 ^ 0xFF00FF00) >> 8
            seed_2 = (seed_2 & 0x0000FFFF) << 16 ^ (seed_2 & 0xFFFF0000) >> 16
            key_2 = seed_2
            u32_key = key_1 + key_2
        else:
            BOOT_MASK = 0x71887503
            wLastSeed = seed
            temp =((( BOOT_MASK & 0x00000800) >> 10) | ((BOOT_MASK & 0x00200000)>> 21)) & 0xFFFF
            if temp == 0:
                wTemp = ((seed | 0x00ff0000) >> 16)
            elif temp == 1:
                wTemp = ((seed | 0xff000000) >> 24)
            elif temp == 2:
                wTemp = ((seed | 0x0000ff00) >> 8)
            else:
                wTemp = (seed | 0x000000ff)

            SB1 = (( BOOT_MASK & 0x000003FC) >> 2)
            SB2 = ((( BOOT_MASK & 0x7F800000) >> 23) ^ 0xA5)
            SB3 = ((( BOOT_MASK & 0x001FE000) >> 13) ^ 0x5A)
            iterations = (((wTemp | SB1) ^ SB2) + SB3)
            for jj in range(iterations):
                wTemp = (((wLastSeed ^ 0x40000000) // 0x40000000) ^ ((wLastSeed & 0x01000000) // 0x01000000) ^ ((wLastSeed & 0x1000) // 0x1000) ^ ((wLastSeed & 0x04) // 0x04)) & 0xFFFFFFFF
                wLSBit = ((wTemp ^ 0x00000001))& 0xFFFFFFFF
                wLastSeed = ((wLastSeed << 1))& 0xFFFFFFFF
                wTop31Bits = ((wLastSeed ^ 0xFFFFFFFE))& 0xFFFFFFFF
                wLastSeed = ((wTop31Bits | wLSBit))& 0xFFFFFFFF

            if BOOT_MASK & 0x00000001:
                wTop31Bits = (((wLastSeed & 0x00FF0000) >>16) | ((wLastSeed ^ 0xFF000000) >> 8) | ((wLastSeed ^ 0x000000FF) << 8) | ((wLastSeed ^ 0x0000FF00) <<16))& 0xFFFFFFFF
            else:
                wTop31Bits = wLastSeed
        
            wTop31Bits = wTop31Bits ^ BOOT_MASK
            u32_key = wTop31Bits

        u32_key &= 0xFFFFFFFF
        key[0] = (u32_key>>24) & 0xFF
        key[1] = (u32_key>>16) & 0xFF
        key[2] = (u32_key>>8) & 0xFF
        key[3] = (u32_key) & 0xFF
        return key

    def pre_update(self):
        try:
            '''预刷新步骤'''
            #step1: 扩展会话模式  
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x10,0x01],2)
            time.sleep(1)          
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x10,0x03],2)
            if uds_rep[0:2] == [0x50,0x03]:
                logging.info('Enter extended session ok')
            else:
                logging.info('Enter extended session fail') 
            #step2: 安全算法          
            uds_rep,datasize = self.diag.req_and_res_can([0x27,0x01],2)
            if uds_rep[0:2] == [0x67,0x01]:
                logging.info('Request seed1 ok')
            else:
                logging.info('Request seed1 fail')
            seed = uds_rep[2:6]
            key = self.calcKey(seed,3)
            uds_rep,datasize = self.diag.req_and_res_can([0x27,0x02]+key,6)
            if uds_rep[0:2] == [0x67,0x02]:
                logging.info('Send key level1 ok')
            else:
                logging.info('Send key level1 fail')        
            if self.skipprecheck == False:                
                #step3: 检查预刷新条件
                uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0xFD,0x02],4)
                if uds_rep[0:5] == [0x71,0x01,0xFD,0x02,0x00]:
                    logging.info('Check programming precondition ok')
                else:
                    logging.info('Check programming precondition fail')
                    self.exitflash = True 
            #step4: 停止诊断服务
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x85,0x82],2)
            #step5: 通信停止收发
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x28,0x83,0x01],3)
            #step6: 编程会话模式
            uds_rep,datasize = self.diag.req_and_res_can([0x10,0x02],2)
            if uds_rep[0:2] == [0x50,0x02]:
                logging.info('Enter programming session ok')
            else:
                logging.info('Enter programming session fail')
            #step7: 安全算法
            time.sleep(0.1)
            uds_rep,datasize = self.diag.req_and_res_can([0x27,0x35],2)
            if uds_rep[0:2] == [0x67,0x35]:
                logging.info('Request seed35 ok')
            else:
                logging.info('Request seed35 fail')
            seed = uds_rep[2:6]
            key = self.calcKey(seed,2)
            uds_rep,datasize = self.diag.req_and_res_can([0x27,0x36]+key,6)
            if uds_rep[0:2] == [0x67,0x36]:
                logging.info('Send key level35 ok')
            else:
                logging.info('Send key level35 fail')
            if self.exit:
                #step8-1: 退出boot模式
                uds_rep,datasize = self.diag.req_and_res_can([0x10,0x01],2)
                if uds_rep[0:2] == [0x50,0x01]:
                    logging.info('退出boot模式 ok')
                else:
                    logging.info('退出boot模式 fail')
        except:
            logging.info('Berfor update, CAN Communication Error!')

    def flash_driver_update(self):
        #step:1/2/3: 34、36、37服务更新flash Drive 
        self.cal = False  
        self.erash = False           #No need erash
        check_crc_flash, start_address, segment_size = self.download(self.flashdrive_path)   
        #step4: 检查传输行列
        crc_flash=[0,0,0,0]
        crc_flash[0] = ((check_crc_flash)>>24) & 0xFF
        crc_flash[1] = ((check_crc_flash)>>16) & 0xFF
        crc_flash[2] = ((check_crc_flash)>>8) & 0xFF
        crc_flash[3] = (check_crc_flash) & 0xFF
        try:
            uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0xFD,0x03]+crc_flash,8)
            if uds_rep[0:5] == [0x71,0x01,0xFD,0x03,0x00]:
                logging.info('Check FlashDrive transmit sequence ok')
            else:
                logging.info('Check FlashDrive transmit sequence fail')
        except:
            logging.info('31 server check FlashDrive transmit sequence no response!')

    def cal_update(self):
        self.progress_number = 0 
        self.cal = True             #cal file
        self.erash = True           #need erash
        time.sleep(0.1)
        #step1/2/3/4: 31、34、36、37服务下载程序
        check_crc_app, startaddress, size  = self.download(self.cal_path)
        self.erash = False 
        #step5: 获取擦除地址和长度
        eras_startaddress = [0x00,0x00,0x00,0x00]
        eras_startaddress[0] = ((startaddress)>>24) & 0xFF
        eras_startaddress[1] = ((startaddress)>>16) & 0xFF
        eras_startaddress[2] = ((startaddress)>>8) & 0xFF
        eras_startaddress[3] = (startaddress) & 0xFF
        erash_size = [0x00,0x00,0x00,0x00]
        erash_size[0] = ((size)>>24) & 0xFF
        erash_size[1] = ((size)>>16) & 0xFF
        erash_size[2] = ((size)>>8) & 0xFF
        erash_size[3] = (size) & 0xFF       
        #step6: 检查传输行列
        if self.crcerror:
            crc_app=[0,0,0,0]
        else:
            crc_app=[0,0,0,0]
            crc_app[0] = ((check_crc_app)>>24) & 0xFF
            crc_app[1] = ((check_crc_app)>>16) & 0xFF
            crc_app[2] = ((check_crc_app)>>8) & 0xFF
            crc_app[3] = (check_crc_app) & 0xFF
        try:
            uds_rep,datasize = self.diag.req_and_res_can([0x3E,0x80],2)
            uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0xFD,0x04,0x44]+eras_startaddress+erash_size+crc_app,17)
            if uds_rep[0:5] == [0x71,0x01,0xFD,0x04,0x00]:
                logging.info('Check Cal transmit sequence ok')
            else:
                logging.info('Check Cal transmit sequence fail')
                # logging.info('crc value is response: '.format(hex(crc_app)))
                # logging.info('31 response: '.format(hex(uds_rep)))   
        except:
            logging.info('Check Cal transmit and programming Dependencies no response!')
            self.finish = False                
        
    def post_update(self):
        try:
            #step20: 检查软硬件一致性
            #step21: 硬件重启
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x11,0x01],2)
            if uds_rep[0:2] == [0x51,0x01]:
                logging.info('Hardware reset ok') 
                self.finish = True
            else:
                self.success_flag = False
                self.finish = True 
                logging.info('Hardware reset fail')

            time.sleep(5)
            '''后刷新步骤'''
            #step22: 恢复通信
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x28,0x80,0x01],3)
            #step23: 开启DTC 
            uds_rep,datasize = self.diag.req_and_res_can_fun([0x85,0x81],2)
            #step24: DID读取刷写次数 
        except:
            self.success_flag = False
            self.finish = False 
            logging.info('After update, CAN Communication Error!')

    def post_abnormal_termination_startup(self):            #
        for i in range(6):
            uds_rep,datasize = self.diag.req_and_res_can([0x10,0x01],2)
            if len(uds_rep) < 2:
                self.normal =False
            if uds_rep[0:2] == [0x50,0x01]:
                self.normal = True
                break
            else:
                self.normal = False

    def run(self):
        self.pre_update()
        if self.exitflash == False:
            if self.skipflash == False:
                self.flash_driver_update()
            self.cal_update()
            self.post_update()

    def set_stop(self,flag):
        if self.app:
            self.stop_flag = flag

if __name__ == '__main__':
    import os
    root = os.getcwd()
    if Set_up.ECU_type == 'Front':
        project_DBC_File_CAN = os.path.join(root, r'data\AFD1.dbc')
    else:
        project_DBC_File_CAN = os.path.join(root, r'data\AFD2.dbc')

    # project_DBC_File_PCAN = os.path.join(root, r'data\CAN1_4.dbc')
        
    modbus = Modbus(Set_up.modbusCOM)
    modbus.write_single_coil(DO_Address_single.Y4.value,0xFF00)
    modbus.write_single_coil(DO_Address_single.Y5.value,0xFF00)
    modbus.write_single_coil(DO_Address_single.Y0.value,0xFF00)
    modbus.write_single_coil(DO_Address_single.Y1.value,0xFF00)
    logging.info("Close PLC Y4 to select Right Rear Radar".center(50, '-'))

    print(project_DBC_File_CAN)
    # print(project_DBC_File_PCAN)
    channel_set_list = [
                    {'drive_type':'USE_TS_TC1014_DEVICE',
                    'configure_baudrate':{'CAN_Type':'CANFD','ArbRate':500,'DataRate':2000,'TermResistor':True},
                    'ChannelIndex' : TSMasterAPI.CHANNEL_INDEX.CHN1
                    },
                    {'drive_type':'USE_TS_TC1014_DEVICE',
                    'configure_baudrate':{'CAN_Type':'CANFD','ArbRate':500,'DataRate':2000,'TermResistor':True},
                    'ChannelIndex' : TSMasterAPI.CHANNEL_INDEX.CHN2
                    },
                    {'drive_type':'USE_TS_TC1014_DEVICE',
                    'configure_baudrate':{'CAN_Type':'CANFD','ArbRate':500,'DataRate':2000,'TermResistor':True},
                    'ChannelIndex' : TSMasterAPI.CHANNEL_INDEX.CHN3
                    },
                    {'drive_type':'USE_TS_TC1014_DEVICE',
                    'configure_baudrate':{'CAN_Type':'CANFD','ArbRate':500,'DataRate':2000,'TermResistor':True},
                    'ChannelIndex' : TSMasterAPI.CHANNEL_INDEX.CHN4
                    }
                    ]
    '''初始化CAN'''
    project_rbs = TSCAN(channel_set_list)
    project_rbs.load_dbc(Set_up.project_DBC_File_CAN,1)
    project_rbs.register_event_canfd_T_pre(OnCANPreFDevent)
    # project_rbs.load_dbc(Set_up.project_DBC_File_PCAN,2)
    project_rbs.connect_application()
    project_rbs.RBS_Config(Set_up.NetName,Set_up.EnableNode,1)
    # project_rbs.Enable_rbs_message(Set_up.NetName,'FLC_FD3','RCVR_IDB_Fr04_0C7',False,1)
    # project_rbs.RBS_Config(Set_up.PCANNetName,Set_up.PCANEnableNode,2)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_BDCS_Fr01_110', 'BDCS1_PowerMode', 0x02,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_ACU_Fr02_0C4', 'ACU2_LatAccSensorValueV', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_ACU_Fr02_0C4', 'ACU2_LongAccSensorValueV', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_ACU_Fr02_0C4', 'ACU2_VehicleDynYawRateV', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_EPS_Fr01_0B1', 'EPS1_SteerAngleSpdValid', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_EPS_Fr01_0B1', 'EPS1_SteeringAngleValid', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_IDB_Fr01_0E5', 'IDB1_BrakePedalAppliedV', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_IDB_Fr03_0C5', 'IDB3_VehicleSpdValid', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_IDB_Fr03_0C5', 'IDB3_VehicleSpd', 0x00,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_IDB_Fr04_0C7', 'IDB4_FLWhlVelocityValid', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_IDB_Fr04_0C7', 'IDB4_FRWhlVelocityValid', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_IDB_Fr04_0C7', 'IDB4_RLWhlVelocityValid', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_IDB_Fr04_0C7', 'IDB4_RRWhlVelocityValid', 0x01,1)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_VCU_Fr05_0E3', 'VCU5_ActGearValid', 0x01,1)
    # project_rbs.RBS_Stop()
    # wake_ref = project_rbs.send_CANFrame_Cycle(0x611,[0x00,0x51,0x00,0x00,0x00,0x00,0x00,0xAE],500)
    #开启电源
    project_power = power(source=Set_up.source,baudrate=Set_up.baudrate)
    project_power.config()
    project_power.set_vltage(14)
    project_power.Enable(True)
    time.sleep(2)
    if Set_up.CAN_Type == 'CAN':
        SupportFD = 0
        Maxdlc = 8
        session_ref = project_rbs.send_CANFrame_Cycle(0x7DF,[0x02,0x3E,0x80,0x00,0x00,0x00,0x00,0x00],2000,'CAN')
    else:
        SupportFD = 1
        Maxdlc = 15
        session_ref = project_rbs.send_CANFrame_Cycle(0x7DF,[0x02,0x3E,0x80,0x00,0x00,0x00,0x00,0x00],2000,'CANFD')
    diag = TSCAN_UDS(ASupportFD=SupportFD, AMaxdlc=Maxdlc, reqID=0x7C9,resID=0x7D9,AFctID=0x7DF,p2_timeout=500)
    diag_LR = TSCAN_UDS(ASupportFD=SupportFD, AMaxdlc=Maxdlc, reqID=0x7C8,resID=0x7D8,AFctID=0x7DF,p2_timeout=500)
    
    FlashDrivepath =Set_up.FlashDrivepath
    Bootpath = Set_up.Bootpath
    Apppath = Set_up.Apppath
    calpath = Set_up.VehicelcalDatapath
    old_Bootpath = Set_up.old_Bootpath
    old_Apppath = Set_up.old_Apppath
    
    dl = Download(project_rbs,diag,FlashDrivepath,Bootpath,Apppath,skipboot=False,erraddr=False) #SW
    # dl = DownloadCAL(project_rbs,diag,FlashDrivepath,calpath,erraddr=True) #CAL
    # time.sleep(30)
    # logging.info('--------------------------------')
    # logging.info(dl.progress_number)
    # logging.info('--------------------------------')
    # dl.set_stop(True)
    dl.join()

    '''关闭CAN'''
    # project_rbs.stop_FrANFrame_Cycle(wake_ref)
    project_rbs.stop_FrANFrame_Cycle(session_ref)
    project_rbs.write_db_signal(Set_up.NetName, 'FLC_FD3', 'RCVR_BDCS_Fr01_110', 'BDCS1_PowerMode', 0x00,1)
    diag.UDS_can_delete_all()
    project_rbs.dis_connect_application()
    project_power.Enable(False)
    project_power.close()
    modbus.write_single_coil(DO_Address_single.Y4.value,0x0000)
    modbus.write_single_coil(DO_Address_single.Y5.value,0x0000)
    modbus.write_single_coil(DO_Address_single.Y0.value,0x0000)
    modbus.write_single_coil(DO_Address_single.Y1.value,0x0000)
    logging.info("Open PLC Y4 to disconnect Right Rear Radar".center(50, '-'))
    del modbus